package primes;

import java.util.ArrayList;
import java.util.List;

import multithread.PrimeThreadFactory;
import util.ResultsRegister;

/**
 * Verifies that each element of an array is prime 
 * using the concept of parallel processing
 */

public class MultithreadPrime {
	
	private int grainSize;
	private PrimeThreadFactory threadFactory;
	
	private static final int numberOfRepetitions = 40;
	
	public MultithreadPrime(int grainSize, int numberOfThreads) {
		this.grainSize = grainSize;
		threadFactory = new PrimeThreadFactory(numberOfThreads);
	}

	public boolean[] calculate(long[] numbers) throws InterruptedException {
		int start = 0;
		int end = Math.min(start + grainSize - 1, numbers.length - 1);
		boolean[] results = new boolean[numbers.length];
		List<Thread> threads = new ArrayList<Thread>();
		
		do {
			Thread newThread = threadFactory.newThread(new PrimeRunnable(numbers, results, start, end));
			threads.add(newThread);
			newThread.start();
			start = end + 1;
			end = Math.min(start + grainSize - 1, numbers.length - 1);
		} while (start < numbers.length);
		
		for (Thread t : threads) {
			t.join();
		}
		
		return results;
	}
	
	private class PrimeRunnable implements Runnable {
		private long[] numbers;
		private boolean[] results;
		private int start;
		private int end;
		private DefaultPrimeCalculator calculator;
		
		public PrimeRunnable(long[] numbers, boolean[] results, int start,
				int end) {
			this.numbers = numbers;
			this.results = results;
			this.start = start;
			this.end = end;
			calculator = new DefaultPrimeCalculator();
		}
		
		@Override
		public void run() {
			calculator.prime(numbers, results, start, end);
		}
	}
	
	/**
	 * A test
	 * @param args
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws InterruptedException {
		checkArguments(args);
		int grainSize = Integer.parseInt(args[0]);
		int numberOfThreads = Integer.parseInt(args[1]);
		
		ResultsRegister register = new ResultsRegister(System.out);
		MultithreadPrime p = new MultithreadPrime(grainSize, numberOfThreads);
		long[] numbers = getNumbers();
		
		executeExperiment(register, p, numbers);
		System.out.printf("%f\n", register.getAverage());
		System.out.printf("%f\n", register.getStandardDeviation());
	}

	private static void executeExperiment(ResultsRegister register,
			MultithreadPrime p, long[] numbers) throws InterruptedException {
		long[] executionTimes = new long[numberOfRepetitions];
		for (int i = 0; i < numberOfRepetitions; i++) {
			register.markStart();
			p.calculate(numbers);
			register.markEnd();
			executionTimes[i] = register.getExecutionTime();
		}
		register.markResults(executionTimes);
	}

	private static void checkArguments(String[] args) {
		if (args.length != 2) {
			System.err.println("Invalid number of arguments.");
		}
	}
	
	private static long[] getNumbers() {
		long[] numbers = new long[100000];
		
		for (int i = 0; i < numbers.length; i++) {
			numbers[i] = i;
		}
		
		return numbers;
	}
}
